package com.ezops.poc.parser;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ezops.annotation.EzopsDisplayName;
import com.ezops.model.EzopsOTModel;
import com.ezops.poc.bo.ClientTradeModel;
import com.ezops.poc.bo.IClientModel;
import com.ezops.poc.dao.FieldMappingDAO;
import com.ezops.poc.util.LoggerUtil;
import com.ezops.poc.util.NoMappingFoundException;

@Component(value = "clientFormatConverter")
public class ClientFormatConverter implements
		Converter<EzopsOTModel, IClientModel> {

	private Logger logger = LoggerFactory
			.getLogger(ClientFormatConverter.class);

	@Autowired
	private FieldMappingDAO mappingProvider;

	/**
	 * Convert the List of ezops model into Client model. for conversion uses
	 * the mapping defined and stored in datatbase.
	 */
	@Override
	public List<IClientModel> convert(List<EzopsOTModel> sourceList)
			throws NoMappingFoundException {
		logger.trace("Conversion from Ezops to Client format starts");

		if (sourceList == null || sourceList.isEmpty()) {
			logger.info("Ezops model is null");
			throw new IllegalArgumentException(
					" Ezops model list can not be null");
		}

		Map<String, String> mapping = loadMapping(sourceList.get(0)
				.getClientId(), sourceList.get(0).getAssetId());

		if (mapping == null || mapping.isEmpty()) {
			throw new NoMappingFoundException(
					" No Mapping found between ezops format and client format. client Id -"
							+ sourceList.get(0).getClientId() + " Asset Id-"
							+ sourceList.get(0).getAssetId());
		}

		List<IClientModel> convertedList = new ArrayList<>(sourceList.size());

		for (EzopsOTModel ezopsModel : sourceList) {
			try {
				ClientTradeModel clienModel = ClientTradeModel.class
						.newInstance();

				Field[] ezopsFields = EzopsOTModel.class.getDeclaredFields();

				for (Field ezopsField : ezopsFields) {

					EzopsDisplayName displaName = ezopsField
							.getAnnotation(EzopsDisplayName.class);
					if (displaName == null) {
						logger.debug("No Maping specified, so skipping the reverse mapping-"
								+ ezopsField.getName());
						continue;
					}
					String clientFieldName = mapping.get(displaName.name());

					if (clientFieldName == null || clientFieldName.isEmpty()) {
						logger.debug("No mapping found for ezops field-"
								+ displaName);
						continue;
					}

					ezopsField.setAccessible(true);
					if (ezopsField.get(ezopsModel) != null)
						clienModel.addField(clientFieldName,
								ezopsField.get(ezopsModel).toString());

				}

				setBasicFields(ezopsModel, clienModel);

				convertedList.add(clienModel);

			} catch (InstantiationException | IllegalAccessException
					| IllegalArgumentException | SecurityException e) {
				logger.error("Exception while mapping fields - "
						+ e.getMessage());
				throw new NoMappingFoundException(
						"Couldn't complete the conversion reason - "
								+ e.getMessage());
			}
		}
		logger.trace("Headers in generated client format - "
				+ convertedList.get(0).getHeaders());
		logger.trace("Conversion from Client to Ezops format ends");
		logger.trace("No of rows converted back to Client format is "
				+ convertedList.size());
		return convertedList;

	}

	/**
	 * Load the mapping defined in DB for client to ezops fields. TODO - Should
	 * be moved to another class in future and inject as strategy into this
	 * class (Different strategy for different client+asset).
	 * 
	 * @param clientId
	 * @param assetId
	 * @return
	 */
	private Map<String, String> loadMapping(int clientId, int assetId) {
		logger.trace("Loading the mapping defined for client Id -" + clientId
				+ " and asset Id - " + assetId);

		Map<String, String> mapping = mappingProvider.getMapping(clientId,
				assetId);
		logger.trace(" Mapping loaded - "
				+ LoggerUtil.getLoggableFormat(mapping));
		return mapping;
	}

	@Override
	public IClientModel convert(EzopsOTModel source)
			throws NoMappingFoundException {
		List<EzopsOTModel> sourceList = new ArrayList<>();
		sourceList.add(source);
		return this.convert(sourceList).get(0);
	}

	/**
	 * Set the basic fields which are listed in super class of ezopsModel.
	 * 
	 * @param ezopsModel
	 * @param clientModel
	 */
	private void setBasicFields(EzopsOTModel ezopsModel,
			IClientModel clientModel) {
		clientModel.addField("action", ezopsModel.getAction());
		clientModel.setClientId(ezopsModel.getClientId());
		clientModel.setClientTradeId(ezopsModel.getClientTradeId());
		clientModel.setAssetId(ezopsModel.getAssetId());
	}

}
